import { JsonPatchOperation } from '../types';

// A simple, non-comprehensive JSON Patch applicator
export function applyJsonPatch(doc: any, patch: JsonPatchOperation[]): any {
  // Deep copy to avoid modifying the original state directly
  let newDoc = JSON.parse(JSON.stringify(doc));

  patch.forEach(({ op, path, value }) => {
    if (!path) return;
    const pathParts = path.split('/').slice(1).map(p => p.replace(/~1/g, '/').replace(/~0/g, '~'));
    if (pathParts.length === 0) return;

    let current = newDoc;
    for (let i = 0; i < pathParts.length - 1; i++) {
        const part = pathParts[i];
        if (current[part] === undefined) {
            // If path doesn't exist for a replace or remove, it's an error in the patch, but we'll be lenient
            if (op === 'replace' || op === 'remove') return;
             // Check if next part is a number to create an array, otherwise an object
            current[part] = /^\d+$/.test(pathParts[i + 1]) ? [] : {};
        }
        current = current[part];
    }

    const finalPart = pathParts[pathParts.length - 1];

    try {
        switch (op) {
        case 'replace':
            current[finalPart] = value;
            break;
        case 'add':
            if (Array.isArray(current)) {
            if (finalPart === '-') {
                current.push(value);
            } else {
                const index = parseInt(finalPart, 10);
                if (!isNaN(index)) current.splice(index, 0, value);
            }
            } else {
            current[finalPart] = value;
            }
            break;
        case 'remove':
            if (Array.isArray(current)) {
            const index = parseInt(finalPart, 10);
            if (!isNaN(index)) current.splice(index, 1);
            } else {
            delete current[finalPart];
            }
            break;
        default:
            break;
        }
    } catch (e) {
        console.error("Error applying JSON patch operation:", {op, path, value}, "on document:", current, "Error:", e);
    }
  });

  return newDoc;
}
